package com.hcms.common.config.shiro;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    // 安全管理器：负责管理所有 Subject，是 Shiro 的核心
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 设置 Realm
        securityManager.setRealm(myRealm());
        // 设置会话管理器
        securityManager.setSessionManager(sessionManager());
        // 设置会话 DAO
        //securityManager.setCacheManager(sessionDao());
        // 设置 RememberMe 管理器
        securityManager.setRememberMeManager(rememberMeManager());

        return securityManager;
    }

    // 域：负责验证 Subject 的身份，并提供与授权数据交互
    @Bean
    public MyRealm myRealm() {
        return new MyRealm();
    }

    // Session 会话管理器
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置全局会话超时时间（毫秒）
        sessionManager.setGlobalSessionTimeout(1800000);    // 30 分钟
        // 是否在会话过期后自动删除
        sessionManager.setDeleteInvalidSessions(true);
        // 是否开启定时调度器检测过期会话
        sessionManager.setSessionValidationSchedulerEnabled(true);

        return sessionManager;
    }

    /*@Bean
    public EnterpriseCacheSessionDAO sessionDao() {
        // 使用企业级缓存 SessionDAo，可以替换为其它实现
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        // 设置缓存名称，根据实际情况配置
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        return sessionDAO;
    }*/

    // 记住我 功能
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();

        SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe");
        rememberMeCookie.setMaxAge(2592000);    // 设置 cookie 的有效期。单位：秒。这里为 30 天
        rememberMeCookie.setHttpOnly(true);
        rememberMeCookie.setPath("/");

        rememberMeManager.setCookie(rememberMeCookie);
        // cookie 加密的密钥
        rememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        //--- 设置安全认证授权对象
        shiroFilter.setSecurityManager(securityManager);

        //-- 设置未登录重定向路径，没有认证或权限的，会被定位到此路径
        shiroFilter.setLoginUrl("/login/");
        //-- 设置未授权重定向路径，没有该权限的，会被定位到此路径
        shiroFilter.setUnauthorizedUrl("/home/noauth");

        // 设置过滤器
        /*
         * 设置过滤器
         *
         *  常用过滤器如下：
         *  anon：无需认证即可访问
         *  authc：必须经过认证才能访问
         *  user：使用记住我后可访问
         *  perms：拥有某个资源的权限才可访问
         *  role：拥有某个角色才可访问
         * */
        Map<String, String> map = new LinkedHashMap<>();
        // 添加允许匿名访问的接口。"anon"：表示允许匿名访问
        // 静态资源允许匿名访问（例如：项目 static 目录下的资源）
        map.put("/login/**", "anon");
        map.put("/static/**", "anon");
        map.put("/pear/**", "anon");

        // 权限拦截，没有该权限会跳转到未授权页面
        //map.put("/system/role/edit/**", "perms[role:edit]");
        //map.put("/system/admin/add/", "perms[admin:add]");

        // 其他接口需要认证。 "authc"：表示要认证后才能访问
        map.put("/**", "authc");

        // 设置过滤链
        shiroFilter.setFilterChainDefinitionMap(map);

        return shiroFilter;
    }

    // shiro 整合 thymeleaf
    @Bean
    public ShiroDialect getShiroDialect() {
        return new ShiroDialect();
    }
}
